<?php
/**
 * Contains DB_Connection class
 *
 * @package tangra_lib
 * @subpackage registry
 */

/**
 *
 */
require_once(TANGRA_MAIN_DIR.'interfaces/i_db_storable.class.php');
/**
 *
 */
require_once(TANGRA_MAIN_DIR.'exceptions/tangra_module_exception.class.php');


/**
 *
 */
require_once(TANGRA_MAIN_DIR.'/registry/registry_key.class.php');


/**
 * Registry_Key_DB uses DB to store keys
 *
 * @package tangra_lib
 * @subpackage registry
 */
class Registry_Key_DB extends Registry_Key implements I_DB_Storable {
	/**
	 * Key ID
	 *
	 * @var integer
	 * @internal
	 */
	private $id;
	/**
	 * Key parent
	 *
	 * @var integer
	 * @internal
	 */
	private $parent = 0;
	/**
	 * DB table name for the registry table
	 *
	 * @var string
	 * @internal
	 */
	private $table_name;


	/**
	 * Constructor
	 *
	 * @param string $table_name DB table name for the registry table
	 */
	function __construct($table_name = 'registry') {
		$this->set_table_name($table_name);
	}


	/**
	 * Sets ID
	 *
	 * @param intger $id
	 */
	public function set_id($id) {
		tangra_if_not_int_throw_e($id);
		$this->id = $id;
	}


	/**
	 * Returns ID
	 *
	 * @return unknown
	 */
	public function get_id() {
		return $this->id;
	}


	/**
	 * Sets parent ID
	 *
	 * @param integer $parent
	 */
	public function set_parent($parent) {
		$this->parent = $parent;
	}


	/**
	 * Returns parent key ID
	 *
	 * @return integer
	 */
	public function get_parent() {
		return $this->parent;
	}


	/**
	 * Sets table name
	 *
	 * @param string $table_name
	 * @throws Tangra_Module_Exception
	 */
	public function set_table_name($table_name) {
		if (ereg("^[a-zA-Z]{1}[a-zA-Z0-9_]{1,100}$", $table_name)) {
			$this->table_name = $table_name;
		} else {
			throw new Tangra_Module_Exception('Invalid table name: '.$table_name);
		}
	}


	/**
	 * Returns table name
	 *
	 * @return string
	 */
	public function get_table_name() {
		return $this->table_name;
	}


	/**
	 * Saves the key in DB. Alias of $this->save()
	 *
	 * @param DB_Connection $dbc
	 */
	public function save_key(DB_Connection $dbc) {
		$this->save($dbc);
	}


	/**
	 * Saves the key in DB
	 *
	 * @param DB_Connection $dbc
	 * @return returns ID of the key on success, false otherwise
	 */
	public function save(DB_Connection $dbc) {
		$ret = false;

		if ($this->get_id()) {
			$ret = $this->update($dbc);
		} else {
			$ret = $this->insert($dbc);
			$this->set_id($ret);
		}

		return $ret;
	}


	/**
	 * Loads key by ID
	 *
	 * @param DB_Connection $dbc
	 * @param integer $id
	 * @return integer Returns ID on success, false otherwise
	 */
	public function load_by_id(DB_Connection $dbc, $id) {
		tangra_if_not_int_throw_e($id);
		$ret = false;

		$table = $this->get_table_name();

		$sql = "select id, key_name, parent, key_value, key_type, full_path from $table where id = $id";
		$rez = $dbc->execute($sql);

		if (!$rez->is_eof()) {
			$rez_obj = $rez->fetch_object();

			$this->set_id($id);
			$this->set_key_name($rez_obj->KEY_NAME);
			$this->set_parent($rez_obj->PARENT);
			$this->set_value(stripslashes($rez_obj->KEY_VALUE));
			$this->set_type($rez_obj->KEY_TYPE);
			$this->set_path(stripslashes($rez_obj->FULL_PATH)); //added in 1.1.0			

			$ret = $id;
		}

		return $ret;
	}

	

	/**
	 * Loads key by parent and key name
	 *
	 * @param DB_Connection $dbc
	 * @param integer $parent ID of the parent
	 * @param string $key_name Name of the key
	 * @return integer Returns key ID on success, false otherwise
	 */
	public function load_by_parent_and_key_name(DB_Connection $dbc, $parent, $key_name) {
		tangra_if_not_int_throw_e($parent);

		$ret = false;

		if (!Registry_Key::is_valid_key_name($key_name)) {
			throw new TE_Registry_Invalid_Key_Name($key_name);
		}

		$table = $this->get_table_name();
		$sql = "select id from $table where parent = $parent and key_name = '$key_name'";
		$rez = $dbc->execute($sql);
		if (!$rez->is_eod()) {
			$rez_obj = $rez->fetch_object();
			$ret = $this->load_by_id($dbc, $rez_obj->ID);
		}

		return $ret;
	}


	/**
	 * Loads key by full path
	 *
	 * @param DB_Connection $dbc
	 * @param string $full_path Full path of the key
	 * @return integer Returns key ID on success, false otherwise
	 */
	public function load_by_full_path(DB_Connection $dbc, $full_path) {
		$ret = false;

		$id = $this->key_exists($dbc, $full_path);
		if ($id) {
			$ret = $this->load_by_id($dbc, $id);
		}

		return $ret;
	}


	/**
	 * Checks if key exists
	 *
	 * @param DB_Connection $dbc
	 * @param string $full_path Full path to the key
	 * @param string $table_name Table name of the registry
	 * @return boolena Returns true if key exist, false otherwise
	 * @throws TE_Registry_Invalid_Path
	 */
	public static function key_exists(DB_Connection $dbc, $full_path, $table_name =  'registry') {
		$ret = false;

		$sql = "select id from $table_name where full_path = '$full_path'";
		$rez = $dbc->execute($sql);
		if (!$rez->is_eof()) {
			$rez_obj = $rez->fetch_object();
			$ret = $rez_obj->ID;
		} else {
			$path_arr = Registry_Key::decompose_key_path($full_path);
	
			if (Registry_Key::is_valid_decomposed_path($path_arr)) {
				$last_key_id = 0;
				foreach($path_arr as $key) {
					$tmp_key = new Registry_Key_DB($table_name);
					$last_key_id = $tmp_key->load_by_parent_and_key_name($dbc, $last_key_id, $key);
					if (!$last_key_id) {
						break;
					}
				}
	
				if ($last_key_id !== false) {
					$ret = $last_key_id;
				}
			} else {
				throw new TE_Registry_Invalid_Path($full_path);
			}
		}

		return $ret;
	}


	/**
	 * Inserts the key into DB
	 *
	 * @param DB_Connection $dbc
	 * @return integer ID of the key. If unsuccessful - returns false
	 * @internal
	 * @throws TE_Exception
	 */
	protected function insert(DB_Connection $dbc) {
		$ret = false;

		$table = $this->get_table_name();

		$id = $dbc->generate_id($table.'_seq');
		$key_name = $this->get_key_name();
		$parent = $this->get_parent();
		$key_value = addslashes($this->get_value());
		$key_type = $this->get_type();
		if (!$full_path) {

		} else {
			$full_path = addslashes($this->get_path());			
		}

		if ($id) {
			$sql = "insert into $table (id, ".
										"key_name, ".
										"parent, ".
										"key_value, ".
										"key_type, ".
										"full_path) ".
									"value ($id, ".
										"'$key_name', ".
										"$parent, ".
										"'$key_value', ".
										"$key_type, ".
										"'$full_path')";
			$dbc->execute($sql);
			$ret = $id;
		} else {
			throw new TE_Exception('ID not generated - '.$table.'_seq');
		}

		return $ret;
	}


	/**
	 * Updates the key data into DB
	 *
	 * @param DB_Connection $dbc
	 * @return Integer ID of the key
	 */
	protected function update(DB_Connection $dbc) {
		$table = $this->get_table_name();
		$key_name = $this->get_key_name();
		$parent = $this->get_parent();
		$key_value = addslashes($this->get_value());
		$key_type = $this->get_type();
		$full_path = addslashes($this->get_path());		

		$sql = "update $table set key_name = '$key_name', ".
									"parent = $parent, ".
									"key_value = '$key_value', ".
									"key_type = $key_type, ".
									"full_path = '$full_path' ".
								"where id = ".$this->get_id();
		$dbc->execute($sql);

		return $this->get_id();
	}


	/**
	 * Returns child keys of key specified by full path
	 *
	 * @param DB_Connection $dbc
	 * @param string $key_full_path Full path of the key
	 * @param string $able_name Table name of registry
	 * @return array
	 */
	public static function get_child_keys_by_full_path(DB_Connection $dbc, $key_full_path, $table_name =  'registry') {
		$ret = false;

		if (!Registry_Key::is_valid_path($key_full_path)) {
			throw new TE_Registry_Invalid_Path($key_full_path);
		}

		if ($key_full_path == '/') {
			$parent_id = 0;
		} else {
			$key = new Registry_Key_DB($table_name);
			$parent_id = $key->load_by_full_path($dbc, $key_full_path);
		}

		if ($parent_id !== false) {
			$ret = Registry_Key_DB::_get_child_keys_by_id($dbc, $parent_id, $table_name);
		}


		return $ret;
	}


	/**
	 * Returns child kes of a key specified by ID
	 *
	 * @param DB_Connection $dbc
	 * @param integer $parent_id Key ID
	 * @param string $table_name Table name of registry
	 * @return array
	 */
	public static function get_child_keys_by_id(DB_Connection $dbc, $parent_id, $table_name =  'registry') {
		tangra_if_not_int_throw_e($parent_id);
		$ret = Registry_Key_DB::_get_child_keys_by_id($dbc, $parent_id, $table_name);

		return $ret;
	}


	/**
	 * Returns child kes of a key specified by ID
	 *
	 * @param DB_Connection $dbc
	 * @param unknown_type $parent_id Key ID
	 * @param string $table_name Table name of registry
	 * @return array
	 * @internal
	 */
	private static function _get_child_keys_by_id(DB_Connection $dbc, $parent_id, $table_name =  'registry') {
		$ret = array();
		$sql = "select id from $table_name where parent = $parent_id order by key_name asc";

		$rez = $dbc->execute($sql);
		while($rez_obj = $rez->fetch_object()) {
			$tmp_key = new Registry_Key_DB($table_name);
			if ($tmp_key->load_by_id($dbc, $rez_obj->ID)) {
				$ret[] = clone $tmp_key;
			}
		}

		return $ret;
	}


	/**
	 * Deletes a key specified by it's ID
	 *
	 * @param DB_Connection $dbc
	 * @param integer $id Key ID
	 * @param string $table_name Table name of registry
	 */
	public static function delete(DB_Connection $dbc, $id, $table_name =  'registry') {
		$childs = Registry_Key_DB::get_child_keys_by_id($dbc, $id, $table_name);

		if ($childs) {
			foreach($childs as $key) {
				Registry_Key_DB::delete($dbc, $key->get_id(), $table_name);
			}
		}

		$sql = "delete from $table_name where id = $id";
		$dbc->execute($sql);
	}


	/**
	 * Deletes key (specified with full path) and it's children
	 *
	 * @param DB_Connection $dbc
	 * @param string $full_path Full path of the key
	 * @param string $table_name Table name of registry
	 */
	public static function delete_key_and_children_with_path(DB_Connection $dbc, $full_path, $table_name = 'registry') {

		$key_id = Registry_Key_DB::key_exists($dbc, $full_path, $table_name);

		if (Registry_Key_DB::key_exists($dbc, $fulle_path, $table_name)) {
			Registry_Key_DB::delete_key_and_children_with_id($dbc, $fulle_path, $table_name);
		}
	}


	/**
	 * Deletes key (specified with ID) and it's children
	 *
	 * @param DB_Connection $dbc
	 * @param integer $id ID of the key
	 * @param string $table_name Table name of registry
	 */
	public static function delete_key_and_children_with_id(DB_Connection $dbc, $id, $table_name = 'registry') {
		$childs = Registry_Key_DB::get_child_keys_by_id($dbc, $id, $table_name);
		foreach($childs as $child) {
			Registry_Key_DB::delete_key_and_children_with_id($dbc, $child->get_id(), $table_name);
		}

		Registry_Key_DB::delete($dbc, $id);
	}

	
	/**
	 * 
	 * Returns full path of the current key
	 * @param DB_Connection $dbc
	 * @param integet $id
	 */
	public static function find_full_path_arr(DB_Connection $dbc, $id) {
		$ret = '/'.implode('/', array_reverse(Registry_Key_DB::_find_full_path_arr($dbc, $id)));
		return $ret;
	}

	
	/**
	 * 
	 * Returns array with full path of the current key
	 * @param DB_Connection $dbc
	 * @param integer $id
	 * @internal
	 */
	private static function _find_full_path_arr(DB_Connection $dbc, $id) {
		$ret = array();
		
		tangra_if_not_int_throw_e($id);
		
		$tmp = new Registry_Key_DB();
		
		if ($tmp->load_by_id($dbc, $id)) {
			$ret[] = $tmp->get_key_name();			
			if ($tmp->get_parent()) {
				$ret = array_merge($ret, Registry_Key_DB::_find_full_path_arr($dbc, $tmp->get_parent()));
			}
		}

		return $ret;	
	}

	public static function load_by_full_path_col(DB_Connection $dbc, $full_path) {
		$ret = false;
		
		$table = $this->get_table_name();
		$full_path = addslashes($full_path);
				
		$sql = "select id from $table where full_path = 'full_path'";
		$rez = $dbc->execute($sql);
		if (!$rez->is_eof()) {
			$rez_obj = $rez->fetch_object();
			$ret = $this->load_by_id($dbc, $rez_obj->ID);
		}
		
		return $ret;
	}	
}